package WizardTD;

import WizardTD.element.Monster;
import lombok.Data;
import processing.core.PApplet;
import processing.core.PImage;
import processing.data.JSONArray;
import processing.data.JSONObject;

import java.io.File;
import java.util.*;

/**
 * @author HustleLr
 * @version 1.0.0
 */
@Data
public class DataConstruct {
    private static final int[][] DIRECTIONS = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    public char[][] chars;

    public String[] layout;
    public LevelAndWaveConfig levelAndWaveConfig;
    public JSONObject config;
    public List<Coordinate> shortestPath;

    /**
     * 波数与怪兽列表
     */
    public Map<Integer, List<Monster>> monsterMap = new HashMap<>();
    /**
     * wave与间隔时间
     */
    public Map<Integer, Double> waveIntervalMap = new HashMap<>();
    private Map<Integer, CountdownTimer> countdownTimerMap = new HashMap<>();
    private Map<Integer, CountdownTimer> waitTimerMap = new HashMap<>();

    private PApplet pApplet;
    public Map<String, PImage> imgMap = new HashMap<>();

    public File folder;
    int lastSpawnFrame;

    public DataConstruct() {
    }

    public DataConstruct(App pApplet) {
        this.pApplet = pApplet;
        lastSpawnFrame = pApplet.frameCount;
    }

    /**
     * 生成二维字符串数组
     */
    public void generateChars() {
        int numRowCols = layout.length;
        chars = new char[numRowCols][numRowCols];
        // 填充字符二维数组
        for (int i = 0; i < numRowCols; i++) {
            String img = layout[i];
            if (img.length() < numRowCols) {
                img = String.format("%-" + numRowCols + "s", img);
            }
            chars[i] = img.toCharArray();
        }
    }


    /**
     * 查找最短路径
     */
    public void findShortestPath() {
        int rows = chars.length;
        int cols = chars[0].length;
        Coordinate start = null;
        Coordinate end = null;
        // Find the end coordinates
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (chars[i][j] == 'W') {
                    end = new Coordinate(i, j);
                }
            }
        }

        if (end == null) {
            return;
        }

        LinkedList<Coordinate> shortestPath = new LinkedList<>();
        int[][] distance = new int[rows][cols];
        Coordinate[][] previous = new Coordinate[rows][cols];
        Queue<Coordinate> queue = new LinkedList<>();
        // Find the nearest 'X' point on the boundary as the start
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) {
                    if (chars[i][j] == 'X') {
                        start = new Coordinate(i, j);
                        queue.add(start);
                        distance[start.row][start.col] = 1;
                    }
                }
            }
        }
        int count = 0;
        // Perform BFS
        while (!queue.isEmpty()) {
            Coordinate current = queue.poll();
            if (current.row == end.row && current.col == end.col) {
                // Reached the end, reconstruct the path
                Coordinate backtrack = current;
                while (backtrack != null) {
                    shortestPath.add(backtrack);
                    count++;
                    backtrack = previous[backtrack.row][backtrack.col];
                }
                break;
            }

            for (int[] dir : DIRECTIONS) {
                int newRow = current.row + dir[0];
                int newCol = current.col + dir[1];
                if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols
                        && (chars[newRow][newCol] == 'X' || chars[newRow][newCol] == 'W') && distance[newRow][newCol] == 0) {
                    distance[newRow][newCol] = 1;
                    previous[newRow][newCol] = current;
                    queue.add(new Coordinate(newRow, newCol));
                }
            }
        }
        Collections.reverse(shortestPath);
        this.shortestPath = shortestPath;
    }

    public void generateMonsterMap() {
        List<Wave> waves = this.levelAndWaveConfig.getWaves();
        Map<Integer, CountdownTimer> countdownTimerMap = this.getCountdownTimerMap();
        Map<Integer, CountdownTimer> waitTimerMap = this.getWaitTimerMap();
        int waveNum = waves.size();
        for (int i = 0; i < waveNum; i++) {
            Wave wave = waves.get(i);
            // 波数计时器
            CountdownTimer countdownTimer = new CountdownTimer(wave.getDuration());
            countdownTimerMap.put(i, countdownTimer);
            List<Monster> monsters = wave.getMonsters();
            // 波的间隔等待时间
            CountdownTimer countdownTimer1 = new CountdownTimer(wave.getPre_wave_pause());
            waitTimerMap.put(i, countdownTimer1);
            // 怪物总数
            int allQuantity = monsters.stream().mapToInt(Monster::getQuantity).sum();
            levelAndWaveConfig.setAllMonsterNums(levelAndWaveConfig.getAllMonsterNums() + allQuantity);
            wave.setAllQuantity(allQuantity);
            // 间隔秒数
            Integer duration = wave.getDuration();
            double intervalSecond = 1.0 * duration / allQuantity;
            waveIntervalMap.put(i, intervalSecond);
            List<Monster> monsterList = new ArrayList<>(allQuantity);
            monsterMap.put(i, monsterList);
        }
    }

    /**
     * 处理图片资源map
     */
    private void processImageMap() {
        folder = new File(pApplet.sketchPath("src/main/resources/WizardTD/"));
        File[] files = folder.listFiles();
        assert files != null;
        for (File file : files) {
            if (file.isFile()) {
                String name = file.getName();
                imgMap.put(name.substring(0, name.length() - 4), pApplet.loadImage(file.getPath()));
            }
        }
    }

    /**
     * 读取Json配置
     *
     */
    public void readJsonConfig() {
        config = pApplet.loadJSONObject(App.configPath);
        LevelAndWaveConfig levelAndWaveConfig = new LevelAndWaveConfig();
        levelAndWaveConfig.setLayout(config.getString("layout"));
        levelAndWaveConfig.setAllMonsterNums(0);
        this.layout = pApplet.loadStrings(levelAndWaveConfig.getLayout());
        levelAndWaveConfig.setInitial_tower_range(config.getInt("initial_tower_range"));
        JSONArray wavesJsonArray = config.getJSONArray("waves");
        List<Wave> waves = new ArrayList<>();
        // 遍历waves数组
        for (int i = 0; i < wavesJsonArray.size(); i++) {
            JSONObject waveJsonObject = wavesJsonArray.getJSONObject(i);
            Wave wave = new Wave();
            wave.setDuration(waveJsonObject.getInt("duration"));
            wave.setPre_wave_pause(waveJsonObject.getFloat("pre_wave_pause"));
            wave.setWaveTimer(wave.getDuration());
            // 获取monsters数组
            JSONArray monstersJsonArray = waveJsonObject.getJSONArray("monsters");
            // 遍历monsters数组
            List<Monster> monsters = new ArrayList<>();
            for (int j = 0; j < monstersJsonArray.size(); j++) {
                JSONObject monsterJsonObject = monstersJsonArray.getJSONObject(j);
                Monster monster = new Monster();
                monster.setType(monsterJsonObject.getString("type"));
                monster.setHp(monsterJsonObject.getInt("hp"));
                monster.setSpeed(monsterJsonObject.getInt("speed"));
                monster.setArmour(monsterJsonObject.getDouble("armour"));
                monster.setMana_gained_on_kill(monsterJsonObject.getInt("mana_gained_on_kill"));
                monster.setQuantity(monsterJsonObject.getInt("quantity"));
                monsters.add(monster);
                wave.setMonsters(monsters);
            }
            waves.add(wave);
        }
        levelAndWaveConfig.setWaves(waves);
        levelAndWaveConfig.setWaveNums(waves.size());
        this.levelAndWaveConfig = levelAndWaveConfig;
    }

    /**
     * 生成数据Data
     */
    public void generateData() {
        readJsonConfig();
        processImageMap();
        generateChars();
        findShortestPath();
        generateMonsterMap();
    }

}
